{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "256d3948",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-2/state-schema.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239426-lesson-1-state-schema)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f118fabe-37b7-4cd4-b7a4-9b0fc3875ca3",
   "metadata": {},
   "source": [
    "# State Schema \n",
    "\n",
    "## Review\n",
    "\n",
    "In module 1, we laid the foundations! We built up to an agent that can: \n",
    "\n",
    "* `act` - let the model call specific tools \n",
    "* `observe` - pass the tool output back to the model \n",
    "* `reason` - let the model reason about the tool output to decide what to do next (e.g., call another tool or just respond directly)\n",
    "* `persist state` - use an in memory checkpointer to support long-running conversations with interruptions\n",
    " \n",
    "And, we showed how to serve it locally in LangGraph Studio or deploy it with LangGraph Cloud. \n",
    "\n",
    "## Goals\n",
    "\n",
    "In this module, we're going to build a deeper understanding of both state and memory.\n",
    "\n",
    "First, let's review a few different ways to define your state schema."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b9a896f4-8509-456a-9a25-46532342f459",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f7927b0-9909-4e54-b997-ac49c1aeaa09",
   "metadata": {},
   "source": [
    "## Schema\n",
    "\n",
    "When we define a LangGraph `StateGraph`, we use a [state schema](https://langchain-ai.github.io/langgraph/concepts/low_level/#state).\n",
    "\n",
    "The state schema represents the structure and types of data that our graph will use.\n",
    "\n",
    "All nodes are expected to communicate with that schema.\n",
    "\n",
    "LangGraph offers flexibility in how you define your state schema, accommodating various Python [types](https://docs.python.org/3/library/stdtypes.html#type-objects) and validation approaches!\n",
    "\n",
    "## TypedDict\n",
    "\n",
    "As we mentioned in Module 1, we can use the `TypedDict` class from python's `typing` module.\n",
    "\n",
    "It allows you to specify keys and their corresponding value types.\n",
    " \n",
    "But, note that these are type hints. \n",
    "\n",
    "They can be used by static type checkers (like [mypy](https://github.com/python/mypy)) or IDEs to catch potential type-related errors before the code is run. \n",
    "\n",
    "But they are not enforced at runtime!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "eedb39f0-af0f-4794-bc16-65980d278b59",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing_extensions import TypedDict\n",
    "\n",
    "class TypedDictState(TypedDict):\n",
    "    foo: str\n",
    "    bar: str"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5a71661-1086-455f-a5e0-a6d104034a95",
   "metadata": {},
   "source": [
    "For more specific value constraints, you can use things like the `Literal` type hint.\n",
    "\n",
    "Here, `mood` can only be either \"happy\" or \"sad\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4ad9749c-b127-433f-baa3-189a9349e9f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Literal\n",
    "\n",
    "class TypedDictState(TypedDict):\n",
    "    name: str\n",
    "    mood: Literal[\"happy\",\"sad\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1a9152d-1728-4a67-9e23-1ef622525047",
   "metadata": {},
   "source": [
    "We can use our defined state class (e.g., here `TypedDictState`) in LangGraph by simply passing it to `StateGraph`.\n",
    "\n",
    "And, we can think about each state key as just a \"channel\" in our graph. \n",
    "\n",
    "As discussed in Module 1, we overwrite the value of a specified key or \"channel\" in each node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "2f7a0d6d-f70b-44ed-86e3-7cdb39873ba4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOYAAAFNCAIAAACbpIR1AAAAAXNSR0IArs4c6QAAIABJREFUeJztnXdAE2fjx58ssoGQQBhhOlARhaoVF1jrRKDU0eKo1r27tPpW/XWq5dVa1Fp3Ha21SqviQOtstTjAUUWLIiAbGUlISIDs/P64vry8GBBrLs/d5fn8lVxyl+/lPnny3HPP8xzNarUCBII80GEHQCCeD6QsgmQgZREkAymLIBlIWQTJQMoiSAYTdgA4WMzWqhJdg8bcUGc2m60GnQV2onbhwqFzeHSekMl3Y4h92LDjwIHmVO2yJpPlYZbm8b360kcNviFcNpfOc2W4e7oYGsmhrNVqrVOaGjQmNo9RXaIL6S4ICef7duDCzuVQnEjZG2eVD29oZJ24IeH8wK582HFeFLXc+Pi+VvnEUKc09Y8XSwM4sBM5CKdQ9vF97bkfqnrGuEfFimFnsT9leQ1XTyi8gzjRYzxhZ3EE1Ff2xlllbZVh8BteLmwqn2sW5dT//nPNhKX+bC4DdhZ8obiyty7UGvUWShauT6OpNf60tnTap0EsSv84qazshYNVXD6jf7wEdhCHsmvl4wlLA/iulG0LouzP8e5lFcuF7my+AgAm/Svwp7UlsFPgCDWVLS9oVDzRO8npSAu4AkbsdJ+LB6tgB8ELair7x5Ga8IHusFNAwzeEW19nLsqphx0EFyio7KPbGpHUxdPPSS8OYfSPF189oYCdAhcoqGzen5r+CU7RRNAGYh92UDde3h0N7CD2h2rKVpfptLVmoTvLMR/35MmTiooKWKu3jTSQk3dbi9PGIUI1ZQvv1QeHO+hibFlZWUJCQk5ODpTVn0lId/7j+xSszlJN2ZpyfYeeDlLWZDL9s1ZtbK1/vHo7odFp3fq6FuZQraCl2qWEbUsLZqwKZrnY+aeo0+mSk5MvX74MAIiMjFyyZInVak1ISGh6Q1xc3KeffmowGHbu3HnmzJmqqiqJRDJ69Og5c+YwGAwAwBtvvNGhQ4cOHTocPHhQp9Pt2bNnwoQJLVa3b2YAQEaanO/OiBwssvuWIUKpayQGnYVGB3b3FQCwZ8+ekydPzp07VyKRnDx5ksvl8ni8VatWrVy5cu7cub179/bw8AAAMBiMzMzM6OhomUyWm5u7e/duV1fXyZMnYxu5du2aTqdLSUlpaGgIDAx8enW7w3dj1KvNeGwZIpRStkFj4glx2aOKigoul/v2228zmczExERsYZcuXQAAQUFBERER2BIGg7Fv3z4ajYY9LSsru3jxYpOyTCZzzZo1XC63tdXtDt+VWV2mx2njsKBUXdZssnL5uOzRqFGjdDrdokWL8vPz236nUqlMTk5OTEwcMmRIQUGBQvHfxtHu3bs3+eoYGCwanU5z5Cc6AEopy3dl1tYY8dhy//79N27cqFAokpKSVq1aZTKZbL5NoVBMmjQpKytr3rx533zzTdeuXc3m//4vO9hXAIC21sTmUeoQU61iwOEzjDqL2WxlMOxftPTv3z8qKuqnn35KSUnx8fGZMWPG0+85fPiwUqncu3evt7c3AMDb27u4uNjuSdpPfZ2Jel26qPYTDAzj16ttF4EvgsFgAADQ6fRJkyZ5eno+fPgQAMDhcAAANTU1TW9TqVQikQjzFXvaRoPM06vbHSsAbhIHXVVxGFT7CbqKmI/v1UfE2LlPzMGDBy9duhQbG1tTU1NTU9OtWzcAgFQq9fPz279/P5fLVavVSUlJvXv3Tk1N3bp1a8+ePS9evHjlyhWLxaJSqdzdbeR5enU22879Iu5lqF/+HJe2CIhQrZQNCRcU3rP/JR+ZTGYwGFJSUtLS0pKSkt566y0AAI1GW7NmDZ/P/+qrr06cOKFUKocMGTJz5syff/55xYoVRqNx7969QUFBhw4dsrnNp1e3b+bSRw3egRzqjVCg2qUEAMDRLeVxM7xZbIoPgXomN84qea6MsCg32EHsDNUqBgCA4DD+9VPKQa+32r87Pj5eo7HRxalHjx7Z2dlPL3dzczt27Ji9Y7YkIyNj5cqVTy+3Wq1Wq5VOt1FYnj171sXFxebWGuvNdy+pZq4OwSEpZChYygIAdn9c+OYS/9ZOlisrKy2W55hrg06nN51R4YdOp7NZN7BYLBaLhcm0sS8+Pj5Nly1acOFglU8Qt1uUKw5JIUNNZfNua2oq9P3jnG7gF4ZaYbxyTB473Qd2EFygWt0co9NLQqPemv2HCnYQOBxcVzJ0ohR2CrygprIAgJixnvl3tfl3qNb17pmkfl0aP9vXhUPZI0vNikETv+57EhLO7/wSBat0NklNKR0+WeruafucjBpQ9reIMXKqz+N7DTfO2rnJk4Coagzb/1Uw8DUJtX2lfimLcfti7b0Mdf94cadIIews9qdRa75yQm5otAydKKVwfaAJp1AWAFCnNF49oTA0moPC+MHd+UIRFa68lzxsqCxuzP5DPSBe0rWvs1R+nEVZjOoy3YNMTeH9eg6P7h3C4QmYPFeG0J1pJknPfYvRolGZ6uvMAFjvZdT5duB0ihR2cxpZMZxL2SZqyvVVJbp6lamhzsxg0jQqO3f+ys3N9fX1FQrtXA/h8pkuXBrfleEqYQV24TFZ1K8GPI2TKos3c+bMmTVrVu/evWEHoSDO+DNFkBqkLIJkIGVxwdfXF5u+AGF3kLK4UFFRYSZLMwTZQMriAo/Ha61bIOIFQcriQkNDA2qKwQmkLC64u7vbHEeAeHHQ14oLKpXquQY+INoPUhYXZDIZajHACaQsLpSVlaEWA5xAyiJIBlIWF4RCIWrkwgmkLC5oNBrUyIUTSFlcQKUsfiBlcQGVsviBlEWQDKQsLnh5eaGrXziBvlZcqK6uRle/cAIpiyAZSFlc8PPzQxdscQIpiwvl5eXogi1OIGURJAMpiwuoJxd+IGVxAfXkwg+kLIJkIGVxAQ0Kxw+kLC6gQeH4gZRFkAykLC6geQzwAymLC2geA/xAyuKCVCpFPblwAn2tuFBVVYV6cuEEUhZBMpCyuODm5oZOv3ACKYsLarUanX7hBFIWF1C3GPxAyuIC6haDH0hZXEClLH4gZXEBlbL4gZTFBbFYjC4l4AS6VZ09GTFiBJPJZDAYtbW1PB4Pe+zi4vLLL7/AjkYdmLADUAoul1tWVoY9bmxsBADQaLTZs2fDzkUp0J+XPYmNjW1xBUEmk7355pvwElEQpKw9GT9+vJ+fX9NTGo02cuRIV1fnupM33iBl7YlIJBo5cmTTU5lMNnHiRKiJKAhS1s4kJSUFBARgj0eOHGn3+9sjkLJ2xt3dffjw4TQaDRWxOOEsLQYGnUVertc1OqIP68CXxl6/WNi3b9+aYnoNqMf74+g04O7Fcvd0wfuDCIJTtMue/aGy8K96nxAeoOK+CtyZZXkNAhEzMsY9uDsfdhzcobiyZpP1yOby0D5uwd0pXqc0myzn91f0HiYK6kZxaymu7OFvyroP9PAN4cEO4iBOfVc6KFHiG8KFHQRHqHz6VZCtdZO4OI+vAIB+8V63L6pgp8AXKisrrzCwuc7VA9Dd06UoB/cTPrhQWVldvdlN7Czn0Rg0Gs07kKOWG2EHwREqK2vUW8wWKtfUbaJVm2h0Ko+UpLKyCEqClEWQDKQsgmQgZREkAymLIBlIWQTJQMoiSAZSFkEykLIIkoGURZAMpCyCZCBl7Uz6qbRXXu2tUMhfcDvZ2X9+8ulSO4WiFEhZInL5j4vLPlpkMBpgByEizjJckSzodLotW78+cfIImoWuNZCy/0P8a4Pfe/ejjIzfrmdm8PmC+LixU6fMwl7KeXB/2/YNubk5HA63f7/oefPedxX+PQ1MXn7uN5vX5ebmiD0k/v6BzTd47PgvqT/vl8urvb19Xx0y8s033mKz2W0EKC0rvnnz+lfrtqSkrMFzR0kMUrYlyf/+5O2pc5KSpv7++7m9+7aHdu4aFTWwqOjx4iVzg4I6LP3wE7Wqds/ebdXVleu/2goAKCkpev+D2W6u7rNmLmQwmN//sLNpU3v37fj5l/1jXk8KDAwpLS06lPp9WXnJ8n993san+3j7fbfrEJdL5cFbLwhStiWxo16bNHEaAKBjh87pp9Kybl6Lihq4/8fv6HT62n9vFgqEAACh0HVN8sd3797u2fOlbTs20mn0bzfvdXcXAQDodPqGjckAALm85scDu1euWB0T/Sq2ZbHYM2XDlwsXLGkqnp9GIBA4cF9JCVK2JRzO3yUcg8Hw9PRSyGsAAHfu3oqM7IP5CgDo06cfACD3UU5oaLcbN64lJIzDfAUAMJl/f6W3bmWaTKbVa1auXrMSW4INZpbXVLehLOKZIGXbgslgmi1mAEB9vdbdTdS0XCh0xcpRhVJuMpl8vH2fXlehlAMA1qze4OUpbb7c11fmkOyUBSnbLiQSr7o6ddPT2lolAEAgEGIeY09bIPxPURoQEOTApNQHtaS0i7CwHnfu3tLpdNjTy5cvAADCwyP4fL6fn//vl84bjS0HtUZG9qHRaEfTDjUtweb1RrwgSNl2MXnidJ2ucdlHi85f+PXAT3u379wUGdE7omcvAMDUKbMrKsoWLpp2NC312PFfDqX+gK0i8/Mf83rS1auXl698/9TpYz/s/27ylMRHeQ9h7wrpQRWDdiGTBaxN3rxj1zdr133G5fKGDY2dO+c9bI75YUNHabWa1NQftu/YGBQY0q1beGlpMbbWgvkfeHlJjx49dOPGNbFYMmjgK54SL9i7QnqoPCfX+QNVYj9uxwjnOj0/vLFozEKZqwdlCyPK7hhhuX49Y/WXK22+tHnTnsDAYIcnIhlIWUcTEdF7x/YDNl9C1Yb2gJR1NBwOx2Y7LqKdoBYDBMlAyiJIBlIWQTKQsgiSgZRFkAykLIJkIGURJAMpiyAZSFkEyUDKIkgGlZXluzLplL43i01EXi50St/sjMrKCtyZVSXONRCgUWuSl+sFblTuOkJlZf1DuQ1qE+wUDqWyqDG0F8WHlVNZWXdPlw49+Zd+roQdxEHIK3R/XlQMTPSEHQRfqDwqASP3pubOZXXHSKGnL8eFkre0pQFlpV5ba8y9oZ64LIDBpHj1nfrKAgCqS3X3rtTVKYzYzV0bGxs5HA42cou86PV6BoPBZDJF3i50GpB15kYOFrVjPdLjFMo2Z+vWrWFhYdHR0bCD2IElS5asXbvW2eZIdCJl09LSEhMTTSZT0xxEFMBqtf7+++99+vRxnsm8nOUHumPHDqVS2XzOLGpAo9F69eo1evRorVYLO4uDoH4pW1ZWJpPJ7t+/3717d9hZcKSsrEyn03Xs2BF2ENyheCmbkZGxadMmAAC1fQUAyGQyFos1depUypdBFC9lDx8+PHbsWNgpHMf9+/fZbLa/vz+Hw4GdBS+oWcpWVlZ+/fXXAACn8hX7M+nUqZNGo9m1axfsLHhBQWUtFsuMGTNmzpwJOwg0PD09jUZjeno67CC4QLWKwe3bt3v06EGxZoF/RklJSUBAQGVlpbe3N+ws9oRSpez8+fPZbDbyFSMgIAD7TvLy8mBnsScUKWVNJpNKpSooKOjbty/sLISDYuegVFD2/v37arW6X79+znbp8rnYsmXL/PnzYaewA6Q/xgqFYt26dQMGDEC+tk1YWNjatWthp7AD5C5lKysrdTpdUBC6f0a7KCwsDA4m/fy1JC6ZvvrqK7PZjHxtP5ivM2fO1Gg0sLP8c8iqbHFxsZ+fn5+fH+wg5GPXrl1ffvkl7BT/HLJWDGpra0Uip+jRjGgB+UrZpUuXXrhwAfn64owdO7aoqAh2iueGZKXslStXAgIC/P39YQehCOnp6YMGDXJ1JdNNe8ikrMFgoNPp6OKWfdHpdGw2m0Qj4UhTMdi5c+fu3buRr3aHyWRGRUXBTvEckKOUzc/Pr6ysHDhwIOwg1ESj0Zw7d27MmDGwg7QLEihrtVpNJhOLxYIdhMpYLBaLxUKKPzESVAxmzZplMBhgp6A4dDp99+7daWlpsIM8G6Ire/jw4XfffZfP58MOQn1mz55dXFysUqlgB3kGJKgYIBDNIXQpu379elJfDScjx44du3PnDuwUbUFcZQ8dOmQ2m4VCIewgzsWgQYM+/PBD2CnagrgVg5qaGolEQqImbsqg1WrpdDqPx4MdxDYELWWpMTkhSeFyuQ0NDbBTtApBlV2wYMHjx49hp3BSGAzGihUrbt68CTuIbYiobGVlJZfL7dmzJ+wgzktSUtL169dhp7ANceuyCIRNiFjKZmdnq9Vq2CmcHcIeBSIqO2/ePDabDTuFs3P+/PmTJ0/CTmEDwilbVVUVFxdH4Yn7yEJMTIzFYoGdwgaoLosgGYQrZYuLi/Pz82GnQACTyXTp0iXYKWxAOGVPnDjxxx9/wE6BAEwmc8mSJQSsGxBOWalUGhoaCjsFAgAAhg8fTsDLYKguiyAZRBk4MX78eAaDQaPRjEYjNpyDRqPRaLQDBw7AjuZ0jBs3js1mMxiMuro6bL5eBoPBZrN37twJOxogkLIGg6G8vLz5EovF0r9/f3iJnJfCwsKnOyTNmzcPUpyWEKUuGx8f3+JrcnNzmzFjBrxEzku/fv1anHX5+/tPnDgRXqL/gSjKJiUlyWSy5ku6desWGRkJL5HzMn36dHd39+ZL4uLiuFwuvET/A1GUFQgEsbGxTdMae3h4zJo1C3YoJ+Wll14KCwtrOi8PCAiYNGkS7FD/hSjKAgAmTpwYGBiIPe7Ro0dERATsRM7L9OnTxWIx1nc2MTGRUNfPCaQsn8+Pj49nMBgeHh5TpkyBHcepiYyMxG6hKpPJxo8fDzvO/9CuFgOT0dKodcRVkJFDX08/djE4ODjYv5um1oT3x1ksVjcxySahadCYzLh/MQAA8ObYtx/llL42+g2TjqXR4f6RNBoQuLfLxmdcSniQVZf9h1pZaeAKGPaLRxTcxKyKwsaQ7vxew0ReMgL999nkWrr8QZbG1YOlVTnEWcci8WNXFDR2ihREj/FkMNsa89eWsllnlfIKY0SMh9CDZEVR+7FYrGq54Y/DVYPHefp1JMpJcQssFmvaloqArnxZZwHflShN6XbHoDMrKvTn9lfM/CKYzWu1iGxV2cxflXUKU1ScF54hCcTJHaUxYyW+IUS09sg35Z17uwV2E8AO4ggsFuv+VQUL1nds7Q22T79qqw3ycr3z+AoAGDLB59b5WtgpbPDwZp1nAMdJfAUA0Om0mHHeGcfkrb7B5lJ5ud5qda45BHhCZmWxrlFrhh2kJU8KdVw+ZSsDNnGTsIoftNqDzLayWrXZ05/opyN2J6CLQFlJuFlBTQarSOpcI+HcvdguXLrVYrvKavvna9RbjDqccxEPba0RdgQbaGpNllYOHoWpKtLR6Lb/5wl0KQGBaA9IWQTJQMoiSAZSFkEykLIIkoGURZAMpCyCZCBlESQDKYsgGUhZBMlAyiJIBoGUTT+V9sqrvRWKVnudtc316xmz50waMar/mxNGb9iYrK4j4gzUZGHVmpVT3h77j1fPzLo6c/aEUaMHTn4rcf+Pu00mew6jIJCyL0JNTfXKjxezXFzmzHpncMyw9FNpq1evgB3Kebl//05QYMisGQs7d+763e4te/Zus+PGKdIR09PT65OPk/v3i2YwGACA+npt+qk0rVYrEDhLz2hCMX3aPGzunzFjkkpKi86dPzVr5kJ7bdxuysa/Nvi9dz/KyPjtemYGny+Ijxs7dcrfc2coFPKt21Iys66YTKbw7hFz57wXEvL3MIm8/NxvNq/Lzc0Re0j8/QObb/DPOzd37tpcUPBIJPKIjOgzc8YCsVjSRoBBA19peszhcAEAZscMRSUYefm5i96Znrxm045d3xQUPJJKfebMemfAgBjs1ZwH97dt35Cbm8PhcPv3i543731XoSv20sXfzu77fkdV1ZOgwJAWExwdO/5L6s/75fJqb2/fV4eMfPONt9q+mUXzuapE7h4N9fV23EF7VgyS//1Jx46hG1J2Dhsau3ff9uvXMwAAOp3ugyVzb93Omj3rnQ/eWy5X1HywZK5GqwEAlJQUvf/BbIW8ZtbMhePHT36U97BpU7duZy1dtjAoMGTJ4v97Y9zk7OzbHyyZq9O1tw/vjZvXOnUMdXNzb8d7KYher//si3+NGztxw9c7vKU+q9asUKtVAICioseLl8w1Go1LP/xk6luzMjJ+++yzZdgq5y/8+sWq5WIPyaKFH/bp06/gcV7T1vbu27Fj56Yhrwz/cMnHg2OGHkr9fn3K6vbEUNepL/529s7dW4mJb9hx7+xZMYgd9dqkidMAAB07dE4/lZZ181pU1MBz50+VlBSt/2rrS5F9AADh4ZETJyccOXJw6pRZ23ZspNPo327e6+4uAgDQ6fQNG5OxTX2zeV183Jh3Fi3FnvbuHTV12rgbN681L0pb44+M30pKipZ/9IUdd410LFr44ZBXhgMAZs5cOGfu5LvZt6MHDdn/43d0On3tvzcLBUIAgFDouib547t3b3fpErb526969Ihct/ZbrGZVXl6aX/AIACCX1/x4YPfKFatjol/FtiwWe6Zs+HLhgiVNxXNrrF694sbN64Njhr4xfrIdd82eymJ/x9isOJ6eXgp5DQDg7t1bAr4A8xUA4O3tExAQlPsoR6fT3bhxLSFhHOYrNtM59qCy8klxcWF5eenJ9KPNt19dXfXMDI2Njd9uWd8ltNvQV0facddIB/c/x0Iq9cHMAwDcuXsrMrIP5isAoE+ffgCA3Ec5RpNRrVaNGzsR8xUAQP/Pg1u3Mk0m0+o1K1evWYktwYZky2uqn6nstGnzOnXqcij1h23bN86d8669dg2v0y8mg2m2mAEA2nqt23+kxHB1dVPIaxRKuclk8vH2fXrd2loFAGDqlNnRg4Y0X+7h0VZdFuO73Vuqq6s++3QdumUzBovJAgBYLGbsrNTd7b/HQih0xWwWCIQAAG9bx0KhlAMA1qze4OUpbb7c11f29Jtb0LVLWNcuYVar9aeD++JGvy6TBdhlj3BvMfCUeOXk3Gu+RKlUSL28se+utlb59CrYN6jX6wICgp7rsx7m5hxNO5T42vjQzl1fODgFkUi86po1V2NfvkAgxI6FSmVjTLzwP0Xp8x6LJrp0CQMAFDzOs5eyuLfLhoX10GjqHjy4jz0tKMgrLy8ND4/g8/l+fv6/XzpvNLYcJCiTBUil3qd/Pd7Y2IgtMZlMT7+tBSaTaf36Ve7uounT5uOzK6QnLKzHnbu3ms5iL1++AAAID4/o0KEznU4/f+H006tERvah0WhH0w41LWk6KG2g1WqbHj969ABrN7DTTuBfyg59ddSPB/Z8+vmytybPpNPpP/ywy91d9FrCeOyvf82X/7dw0bSRIxPodPrhIz9hq9BotAXzF3/8yYcLFr2dED/OYjafOXty2LDYcWPbmkj6519+zC94FBnR+8jRg9gSkcgjPm4M3jtIIiZPnH7x4pllHy2KjxtbXV257/sdkRG9I3r2otFoo0YmpJ9KM+j1L7/cX6GQZ2ZmiERiAIDMz3/M60mHj/y0fOX7AwcMVijkacdSv1yzsXOnLq19islkeue9Gf6ywLCwHmVlJad/Pd6xQ+ewsB722gvclWUymev+/e2WrV9v3ZZisVh6hEcumL9YJPIAAAwbOkqr1aSm/rB9x8agwJBu3cJLS4uxtQYNfOXL1Rv27N327Zb1fL6gR3hkjx4vtfEpCoX8+x92Yq25f965iS0MCgpByjZHJgtYm7x5x65v1q77jMvlDRsaO3fOe1ilf9HCD11cXM5f+PXmrevdu0d06NBZqVRgay2Y/4GXl/To0UM3blwTiyWDBr7iKWlrGiEGgzE69vXjJ365dv0PT4lXfPzYqW/Najqxe3Fsz8mVdUZp0IGeg+1WmJOCs/vKo2I9iDaZ3JHN5eGDPLyDiJUKb/Z9mr8wxfa0XGS6YKvVaidMirP50pzZ78aNft3hiZyX69czVn+50uZLmzftCQwMxu+jyaQsj8fbsd32bcBchW4Oj+PURET0bu1YtF1teHHIpCydTrfZjotwPBwOB9axoEjnQ4TzgJRFkAykLIJkIGURJAMpiyAZSFkEyUDKIkgGUhZBMpCyCJKBlEWQDNsXbF04NAtwuoEoQjGLRryfsJsHi07B+wc/A58QrtVqtTkayvYhEopYNcXP7nxOMYrua8U+LrBTtITJpikq9LBTOBRlpd7QaG5t9J5tZb382c422k9Ta/DryGVzCVeg+YZwCHjPR1xR1eiDwvitvdpqKevXkXP5cCWewYjF+f1P+o4iYpf2TpHCOoX+YZYKdhAHoVUbr6fX9Bstbu0Nbd3c/q9r6rw72p4xYpHUhcEkXi3PHujqzSq5PuNIVcJcX7E3ce+7eWrPE3cvtl8nvgd1bw6qqTUqn+gy0qpnfhHMdGnVt7aUBQAU/lV/55KqslDHYDqoomCxWgCg0R1SLxFJWWq5Mbg7/+URHkIRywGf+CL8+VvtwxsaGp1Wp3DQjUvNFgud3sptOe2NNICjkhs69hQMSHjGbBXPULYJfaOlHe+yA8nJyRERESNHOmKuF6sFcPgk+/cwm6wmo4NuaZuUlLRhwwZvb29HfJjVyua160SivaMS2FwHHVorzUBnmh32caSDwaQ57B/PZGlksR136NsJsdIgEM+EcMq6ubmxWESvVjoJQUH/cFIjXCGcsmq1+plzGSEcw+PHj+l0whlCuEASiaTtGaIRDqNLly52nOXFXhBOWa1WW1dXBzsFAgAA/vzzTxcXwl3BJpyyUqmUgH9GzkmXLl04HA7sFC0hnBxMJrOkpAR2CgTQarV//fUXAStphFNWKpWiigERkMvlgYGB7XijoyGcsj4+Po8fP4adAgHKyspEIlE73uhoCKdscHBwYWEh7BQI8Pjx45CQENgpbEA4ZQUCQa9eveTyf3gnW4S90Gg0XbsS8ZYThFMWAMDn87OysmCncHZOnz4dHh4OO4UNiKhsnz59bty4ATuFU1NWVsZgMHx8fGAHsQERlY2KiqqpqYGdwqkXgQkpAAALA0lEQVS5c+fOiBEjYKewDRGV9fLyMhqNN2/ehB3EeTly5MiAAQNgp7ANEZUFAMTGxp46dQp2CielvLxcLpf37NkTdhDbEFTZhISEnJycFrdYRziGs2fPTpgwAXaKVmnvQBrH89133+n1+vnz0a0SHYrBYIiJibl27RrsIK1CXGWxpoPMzEzUS8aRbN68mc/nT5s2DXaQViG0DUuXLt21axfsFE6EVqvNysoisq9EV3b8+PEZGRl//fUX7CDOwrJly4hfEyO0sgCANWvWLF++HHYKp+D48eNeXl5RUVGwgzwDQtdlMY4cOVJZWUn8Xz+pUalU77///p49e2AHeTZEL2UBAGPGjNFoNKmpqbCDUJnExMRNmzbBTtEuSKAsVse6ePEi6niAE9OmTdu0aZNQKIQdpF2QQ1kAwLZt23bu3InG2NidL774YsqUKT169IAdpL2QoC7bnLFjx65fv56YU0KQkaVLl44YMeLVV1+FHeQ5IE0pi3H48OHFixdfvXoVdhAqMH/+fNL5Sr5SFmPFihWDBw8eNmwY7CAkZurUqWvXrpVKpbCDPDckK2UxVq9efe7cuX379sEOQkoaGxuHDRu2ePFiMvpK1lIWY9OmTSwWa968ebCDkIm7d+8uW7bswIEDHh5EnGW/PZBYWQDAr7/+umfPnu+//56AM0QQkAMHDpw/f3737t2wg7wQ5FYWAJCfnz9lypStW7cStksyQVi+fLlYLF68eDHsIC8K6ZXFWLFiRWBg4OzZs2EHISKlpaXJyckJCQmEHc71XFBEWQDA9u3bs7KytmzZgioJzfnll1/279+/detWYg6X/QdQR1lsXOj8+fOTk5Ojo6NhZyEE69evNxgMH330Eewg9oSUjVytERERcfXq1TNnznz88cews0Dm8uXLL7/88ssvv0wxX6mmLMbq1av79u0bHR1969Yt2FngsGHDhqNHj167dm3QoEGws9gfCioLABg9evTp06ePHz/+xRdfwM7iUK5cuRIdHR0aGpqSkkLAOePtAjWVxSb2+uyzz8LDwwk+XtSObNq06dChQ6dPnx41ahTsLDhCWWUxEhMT09PTT58+vWLFihYvDRkyBFKoF+Xdd99tseTcuXN9+/YNDQ3dtGkTn9/qPbapAcWVxWb//PzzzwcNGjR9+vT09HRsYWJiYl1dHcGHktrk7Nmz9+/f79u3L/ZUq9V+8sknFy5cuHLlCjWaXZ8J9ZXFGDly5O7duzMzM+fPn69QKEpLSwEAeXl5Bw8ehB3t+di6datarTabzbGxsampqaNHjx4+fHhycjKT2d57u5IdZ9lPjM8//zwzM3PEiBE0Gg0AoNPpDhw4EBcXJxAIYEdrF1u2bKmoqMAeV1VVFRYWXrp0CXYoR+MspWwTa9eubf60oqLi66+/hhfnOSguLj516pTZbMae0mi0kydPwg4FAadTtqysrMWSjIyMe/fuQYrzHKSkpFRWVjZf0tjYGBcXBy8RHJxL2fj4eDabjVX7rFYrdrFaoVAQv6A9d+7c7du3m55aLBY6nc7j8XQ6HdRcEKBUH4P2kJ2drVQqa2trnzx5UlFRoVfzhIxObOAl8wnRac00GjAYiDVDqMiLras3qTQ19cbqOn2RlffEy8ddKpVKJBKRSNTUdOA8OJ2yGI1a842zqpxMNUfAcpXymWwmk81kujCYLDrRvg4asBp0ZpPebDZZtPIGrbzBzcslMsYttBc5ph2wO06nrNVq/e1nxaPbdd6dxUIJl8Ei31XNepWutrTOajIOel0c1JXiFw6exrmULcs3/PZzNdedJwlyg53lRWms08uLVRIpa8Rbnk41A68TKfsgq+5qem1IXz+sUZYaKErUpvqGNz+QwQ7iOJxF2bJ83YVD8sCXKNIzvzkaeYNepRm3yBd2EAfhFMoWP6i/lFYbEEFBXzE08oaGGrWTlLXUrwQ1aEy/7quisK8AAKGExxLwzv9UDTuII6C+sunfVQX28oadAnc8/N1qKs1FOVrYQXCH4so+uq0xGOkcgVOMuRX5uV8+qoSdAncoruwfaQrPDmSdyed54QhdmBzWg6w62EHwhcrKFmRruO4cFy4RO1j++PPH/974ht03K/J3y85AypKWR382cN04sFM4FK6QXacwamqNsIPgCJWVLc6pd/V0uuuZAk/e43v1sFPgCBH/NO1CdYnOw5fLYOHym1TWVhw/veFRQRaLyfbzDR01dK6/XzcAwJ4fP/SUBDIYzMybaSazsWvnAWPil3I5fw95uHPv3NnfdtWqnkg9Q6xWvPqLCcS86rJGnDZOBChbytZrzEZ8uhHW1ck375zV0FD3WuwHo0csNJuN3+6a86SqAHv10pUflbUV0yevT4z9IPv+hQu//30nrdt3z+xPXekqECfGLg7tFFVRmYdHNgAAg8WQl+tx2jgRoGwp26Ax0Zm49NI6d2m3gO8xZ9pmBoMJAOjVc1TyhrGZN48ljv4AAOApDpg47jMajRYgC8vO+S03/3ocWGQ06o+d+jokMHLW1G+wGTHkilKcrGWxGQ0aEx5bJgiUVdaos7B4Lnhs+eGjqyp11fIvBjctMZuNqroq7DGLxWnqduPh7lNUkg0AKCy+W9+gGtQ/qWkGFzodr06PTDaDI2BarVYq9f5pDmWVpTNpxgYDHlvWaBXdQgeOHr6g+UIO28YYXQaDZbGYAQC16krMYDzytMBstNSrjFT1lcrK8oRMi6kBly1zXesb1F6ez3HvMQFfBADQNqjwyNMCk97MFVD2sFL59IsnZJiNZjy23CmkT1HJ3dLyB01L9IZnnKH7enei0ei37/6KR54WmAxmviv5hlq0H8r+HKUBHK0ClxPnYa/MfPDoys5970QPmCjkezzMu2axmKdNWtfGKiJ375dfis+8dcxk0od26lenkT94dEUoEOMRr1Gt9wuicp8KyirLYNK8g7kaeYNQwrPvliVi2cJZO0+c2XTx0l5Ao8l8ugyIGv/MtRJHL2YyXf7MPpObnxkc0NPXu7NGq7BvMIx6ZX3HeC88tkwQqNzFOztDlXNT7x0qgR3EcRj1puKbFTNXBcMOgiOULWUBAF36uN66WNrGGxoa6takvG7zJYmHTK5sOa8MACCsS/SEsZ/YK2GjTrt6/Ws2XxLw3G2ersX0nzjslRmtbVBdWR/W39Ve8YgJlUtZAMDVk4ryEqtnsMjmqxaLRaWutPkSADQAbHwzLi5c7PTfLrQRwGQyMpmsp5dzOUIu1/YMBlar9a/zRQu/7miveMSE4soCALYsKegyOIDOoGzbSBNVjxQdu7N6vWq3XxQxof6BHDrJq6YAlxMdQqHTGmgWI+V9dQplO0cK/YJZiqJa2EFwxGq15l8tf+N9P9hBHAH1lQUADEwQS6S06nzKWluWXTllZSDsFA7CKZQFAMSMEfP5ppoCqo3m0zcYcy4WJc71dhXbOFejJNQ//WpO1hllcZ7R1duVzcelk5eDUZbVqSvUkz8KYLk4S9HjdMoCAIof1v+WKnfhs706iJhssjZLqyq01QXK0F7CmLFOdKEEw+mUxcjJrPvrura+zswX81ylfBcuk/i99Sxmi1bRqJU3NNTqfDtwYsZI+G5k/cm9CE6qLMaTwsa8O/WVxfrq4kYXDoPFZbC4DKuJWF8IR8iqq240NJqFEheBKyP0JUFwdx6H74yyYji1ss1p0Jjq1WaDjlizzgMA6HQaV0jnuzJZbCeqsLYBUhZBMtAPF0EykLIIkoGURZAMpCyCZCBlESQDKYsgGf8PvXpHjkF41zIAAAAASUVORK5CYII=",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import random\n",
    "from IPython.display import Image, display\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "def node_1(state):\n",
    "    print(\"---Node 1---\")\n",
    "    return {\"name\": state['name'] + \" is ... \"}\n",
    "\n",
    "def node_2(state):\n",
    "    print(\"---Node 2---\")\n",
    "    return {\"mood\": \"happy\"}\n",
    "\n",
    "def node_3(state):\n",
    "    print(\"---Node 3---\")\n",
    "    return {\"mood\": \"sad\"}\n",
    "\n",
    "def decide_mood(state) -> Literal[\"node_2\", \"node_3\"]:\n",
    "        \n",
    "    # Here, let's just do a 50 / 50 split between nodes 2, 3\n",
    "    if random.random() < 0.5:\n",
    "\n",
    "        # 50% of the time, we return Node 2\n",
    "        return \"node_2\"\n",
    "    \n",
    "    # 50% of the time, we return Node 3\n",
    "    return \"node_3\"\n",
    "\n",
    "# Build graph\n",
    "builder = StateGraph(TypedDictState)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "builder.add_node(\"node_2\", node_2)\n",
    "builder.add_node(\"node_3\", node_3)\n",
    "\n",
    "# Logic\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_conditional_edges(\"node_1\", decide_mood)\n",
    "builder.add_edge(\"node_2\", END)\n",
    "builder.add_edge(\"node_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "724bb640-2b0e-46c1-9416-b5bcdb9c17c8",
   "metadata": {},
   "source": [
    "Because our state is a dict, we simply invoke the graph with a dict to set an initial value of the `name` key in our state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "74e09d32-6a08-4250-b19a-1f701828829d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Node 1---\n",
      "---Node 3---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'Lance is ... ', 'mood': 'sad'}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"name\":\"Lance\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70cc5368-18b8-49c7-b561-41888b092311",
   "metadata": {},
   "source": [
    "## Dataclass\n",
    "\n",
    "Python's [dataclasses](https://docs.python.org/3/library/dataclasses.html) provide [another way to define structured data](https://www.datacamp.com/tutorial/python-data-classes).\n",
    "\n",
    "Dataclasses offer a concise syntax for creating classes that are primarily used to store data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d576fc2c-350b-42ad-89e5-f93ae102dbf8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "\n",
    "@dataclass\n",
    "class DataclassState:\n",
    "    name: str\n",
    "    mood: Literal[\"happy\",\"sad\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64482b93-3c8f-4a30-925f-9be64e4b8b6f",
   "metadata": {},
   "source": [
    "To access the keys of a `dataclass`, we just need to modify the subscripting used in `node_1`: \n",
    "\n",
    "* We use `state.name` for the `dataclass` state rather than `state[\"name\"]` for the `TypedDict` above\n",
    "\n",
    "You'll notice something a bit odd: in each node, we still return a dictionary to perform the state updates.\n",
    " \n",
    "This is possible because LangGraph stores each key of your state object separately.\n",
    "\n",
    "The object returned by the node only needs to have keys (attributes) that match those in the state!\n",
    "\n",
    "In this case, the `dataclass` has key `name` so we can update it by passing a dict from our node, just as we did when state was a `TypedDict`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1e1eda69-916f-4f6e-b400-6e65f73d8716",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def node_1(state):\n",
    "    print(\"---Node 1---\")\n",
    "    return {\"name\": state.name + \" is ... \"}\n",
    "\n",
    "# Build graph\n",
    "builder = StateGraph(DataclassState)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "builder.add_node(\"node_2\", node_2)\n",
    "builder.add_node(\"node_3\", node_3)\n",
    "\n",
    "# Logic\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_conditional_edges(\"node_1\", decide_mood)\n",
    "builder.add_edge(\"node_2\", END)\n",
    "builder.add_edge(\"node_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06beb50a-4878-4d7e-ac6c-d60a0f417eb3",
   "metadata": {},
   "source": [
    "We invoke with a `dataclass` to set the initial values of each key / channel in our state!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "8c042325-e93d-43e1-9ac7-a0e20c2fb08d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Node 1---\n",
      "---Node 3---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'Lance is ... ', 'mood': 'sad'}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke(DataclassState(name=\"Lance\",mood=\"sad\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2405e49b-e786-4bf9-ac85-1fb941d01bcd",
   "metadata": {},
   "source": [
    "## Pydantic\n",
    "\n",
    "As mentioned, `TypedDict` and `dataclasses` provide type hints but they don't enforce types at runtime. \n",
    " \n",
    "This means you could potentially assign invalid values without raising an error!\n",
    "\n",
    "For example, we can set `mood` to `mad` even though our type hint specifies `mood: list[Literal[\"happy\",\"sad\"]]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "522fcc76-abf7-452a-9d7b-000e06942d94",
   "metadata": {},
   "outputs": [],
   "source": [
    "dataclass_instance = DataclassState(name=\"Lance\", mood=\"mad\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f095c3a-96b5-4318-9303-20424b4455e9",
   "metadata": {},
   "source": [
    "[Pydantic](https://docs.pydantic.dev/latest/api/base_model/) is a data validation and settings management library using Python type annotations. \n",
    "\n",
    "It's particularly well-suited [for defining state schemas in LangGraph](https://langchain-ai.github.io/langgraph/how-tos/state-model/) due to its validation capabilities.\n",
    "\n",
    "Pydantic can perform validation to check whether data conforms to the specified types and constraints at runtime."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "62e8720e-217f-4b98-837a-af45c3fa577f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation Error: 1 validation error for PydanticState\n",
      "mood\n",
      "  Value error, Each mood must be either 'happy' or 'sad' [type=value_error, input_value='mad', input_type=str]\n",
      "    For further information visit https://errors.pydantic.dev/2.10/v/value_error\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel, field_validator, ValidationError\n",
    "\n",
    "class PydanticState(BaseModel):\n",
    "    name: str\n",
    "    mood: str # \"happy\" or sad\"\n",
    "\n",
    "    @field_validator('mood')\n",
    "    @classmethod\n",
    "    def validate_mood(cls, value):\n",
    "        # Ensure the mood is either \"happy\" or \"sad\"\n",
    "        if value not in [\"happy\", \"sad\"]:\n",
    "            raise ValueError(\"Each mood must be either 'happy' or 'sad'\")\n",
    "        return value\n",
    "\n",
    "try:\n",
    "    state = PydanticState(name=\"John Doe\", mood=\"mad\")\n",
    "except ValidationError as e:\n",
    "    print(\"Validation Error:\", e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f29913ca-0295-48eb-af4e-cae515dd9a9c",
   "metadata": {},
   "source": [
    "We can use `PydanticState` in our graph seamlessly. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "91db3393-b7f8-46e5-8129-0e7539b2804c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Build graph\n",
    "builder = StateGraph(PydanticState)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "builder.add_node(\"node_2\", node_2)\n",
    "builder.add_node(\"node_3\", node_3)\n",
    "\n",
    "# Logic\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_conditional_edges(\"node_1\", decide_mood)\n",
    "builder.add_edge(\"node_2\", END)\n",
    "builder.add_edge(\"node_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "e96c78be-b483-4fa4-949b-62d4274e97ac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Node 1---\n",
      "---Node 3---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'Lance is ... ', 'mood': 'sad'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke(PydanticState(name=\"Lance\",mood=\"sad\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "8119232a-7d56-4abc-b0ef-18bf5f0cc9fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "#FINISHED\n",
    "#Learned\n",
    "#Pydantic State can be used for validation to any of the keys in our state"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
